/*
 * Hyplnk.c
 *
 *  Created on: Oct 14, 2018
 *      Author: ax
 */
#define __Hyplnk_INIT_HERE__
#include <Log.h>
#include <Device.h>

#include <Hw/Psc.h>
#include <Hw/Hyplnk.h>
#include <Hw/Serdes.h>

#include <ti/csl/cslr_vusr.h>
#include <ti/csl/csl_cpIntcAux.h>

static Isr   Hyplnk_IsrHook = (Isr) NULL;
static void* Hyplnk_IsrArg  = (void*) NULL;
Hyplnk_Cmd   Hyplnk_PeerCmd = Hyplnk_CMD_INVALID;

CSL_VusrRegs* const hHyplnk = (CSL_VusrRegs*) CSL_MCM_CONFIG_REGS;

static void segMap(const Hyplnk_MapEntity* mt) {
    hHyplnk->TX_SEL_CTL
        =  CSL_FMK(VUSR_TX_SEL_CTL_TXIGNMSK, 11)/*TX_ADDR_MASK_0x0FFFFFFF*/
         | CSL_FMK(VUSR_TX_SEL_CTL_TXPRIVIDOVL, 12);//prvid maped to ADDR[31:28]

    hHyplnk->RX_SEL_CTL
        =  CSL_FMK(VUSR_RX_SEL_CTL_RXSEGSEL, 6)/*RX_SEG_SEL_ADDR_27_22*/
         | CSL_FMK(VUSR_RX_SEL_CTL_RXPRIVIDSEL, 12);//prvid maped to ADDR[31:28]

    short i;
    for (i = 0; i < 16; i++) {//prvid map
        hHyplnk->RX_PRIV_IDX = i;
        hHyplnk->RX_PRIV_VAL = (i < 8) ? 13 : 14;
    }
    for (i = 0; i < 64; i++, mt++) {//segment map
        if (mt->valid) {
            hHyplnk->RX_SEG_IDX = i;
            hHyplnk->RX_SEG_VAL = CSL_FMK(VUSR_RX_SEG_VAL_RXSEG_VAL, mt->base)
                                 | CSL_FMK(VUSR_RX_SEG_VAL_RXLEN_VAL, mt->sz);
}    }    }

static void intInit(void) {
    Uint16 i;
    Uint32 reg;

    /*generate interrupt packet to remote DSP when local interrupt event happens*/
    reg = hHyplnk->CTL;
    CSL_FINS(reg, VUSR_CTL_INTLOCAL, 0);
    CSL_FINS(reg, VUSR_CTL_INTENABLE, 1);
    CSL_FINS(reg, VUSR_CTL_INT2CFG, 1);
    CSL_FINS(reg, VUSR_CTL_INTVEC, Hyplnk_ErrorStatusCmd);
    hHyplnk->CTL = reg;

    for (i = 0; i < Hyplnk_ErrorStatusCmd; i++) {
        hHyplnk->INT_CTL_IDX = i;
        reg = CSL_FMK(VUSR_INT_CTL_VAL_INTEN, 0)
            | CSL_FMK(VUSR_INT_CTL_VAL_INTTYPE, 0)
            | CSL_FMK(VUSR_INT_CTL_VAL_INTPOL, 0)
            | CSL_FMK(VUSR_INT_CTL_VAL_ISEC, 0)
            | CSL_FMK(VUSR_INT_CTL_VAL_SIEN, 1)
            | CSL_FMK(VUSR_INT_CTL_VAL_MPS, 0)
            | CSL_FMK(VUSR_INT_CTL_VAL_VECTOR, i);
        hHyplnk->INT_CTL_VAL = reg;
    }
    for (i = 0; i < 8; i++) {
        hHyplnk->INT_PTR_IDX = i;
        hHyplnk->INT_PTR_VAL = 0;
    }
    //clear any pending interrupt
    hHyplnk->INT_CLR = 0xFFFFFFFF;
}

Hyplnk_Status
Hyplnk_setup(
    Hyplnk_MapEntity* hmtlb,
    Uint16            event,
    Loopback          loopback
){
    if (Psc_SOK != Psc_moduleEnable(CSL_PSC_PD_HYPERBRIDGE,
                                    CSL_PSC_LPSC_HYPERBRIDGE))
        return Hyplnk_PWR_FAIL;

    //always force 4 lanes
    hHyplnk->PWR = CSL_FMK(VUSR_PWR_H2L, 7)
                | CSL_FMK(VUSR_PWR_L2H, 7)
                | CSL_FMK(VUSR_PWR_PWC, 1)
                | CSL_FMK(VUSR_PWR_QUADLANE, 1)
                | CSL_FMK(VUSR_PWR_QUADLANE, 0)
                | CSL_FMK(VUSR_PWR_SINGLELANE, 0);
    //enable - disable stop
    CSL_FINS(hHyplnk->CTL, VUSR_CTL_SERIAL_STOP, 0);

    segMap(hmtlb ? hmtlb : &Hyplnk_MapTable[0]);

    intInit();

    /*tell all receivers to ignore close to the first 3uS of data at beginning of training sequence*/
    hHyplnk->SERDES_CTL_STS1 = 0xFFFF0000;

    //serdes configuration
    Serdes_LaneConfiguration lane = {
        .linkSpeed_Kbps     = Serdes_6p25G_BPS_KVAL,
        .testPattern        = Serdes_TEST_DISABLED,
        .txOutputSwing      = 15,
        .txOutputSwing      = 15, /*0~15 represents between 100 and 850 mVdfpp  */
        .txInvertPolarity   = Serdes_TX_NORMAL_POLARITY,
        .rxAlign            = Serdes_RX_COMMA_ALIGNMENT_ENABLE,
        .rxCDR              = 5,
        .rxInvertPolarity   = Serdes_RX_NORMAL_POLARITY,
        .rxTermination      = Serdes_RX_TERM_COMMON_POINT_AC_COUPLE,
        .rxEqualizerConfig  = Serdes_RX_EQ_ADAPTIVE,
        .loopBack           = loopback ? Serdes_LOOPBACK_ENABLE
                                        : Serdes_LOOPBACK_DISABLE   };
    Serdes_Configuration cfg = {
        .inputRefClock_KHz = Serdes_156p25M_REF_CLK,
        .loopBandwidth = Serdes_PLL_LOOP_BAND_MID,
        .lane = { &lane, &lane, &lane, &lane }      };
    Serdes_config(Serdes_VUSR, &cfg, 0x01);
    while (!Serdes_isLocked(Serdes_VUSR));

    /*after initialization, change the delay to default value to improve performance
    hHyplnk->SERDES_CTL_STS1 = 0x092E0000;*/

    Cpintc_route(Hyplnk_CIC, CSL_INTC0_VUSR_INT_O, Hyplnk_HOST_INT);

    return Hyplnk_SOK;
}

void
Hyplnk_post(
    Hyplnk_Cmd code
){
    hHyplnk->SW_INT = code;
}

static void Hyplnk_errorRecovery(void){
    Uint32 status = hHyplnk->STS;
    if (status & CSL_VUSR_STS_SERIAL_HALT_MASK)
        Log_error("[VUSR]: the serial logic is in a halted state due to any of reset, serial_stop, pll_unlock being set.\n");
    if (status & CSL_VUSR_STS_PLL_UNLOCK_MASK)
        Log_error("[VUSR]: the SerDes PLL is not locked to the reference clock. This will prevent any serial operations.\n");
    if (status & CSL_VUSR_STS_RPEND_MASK)
        Log_error("[VUSR]: a remote operation is currently pending or in flight.\n");
    if (status & CSL_VUSR_STS_IFLOW_MASK)
        Log_error("[VUSR]: a flow control enable request has been received\n");
    if (status & CSL_VUSR_STS_OFLOW_MASK)
        Log_error("[VUSR]: the internal flow control threshold has been reached.\n");
    if (status & CSL_VUSR_STS_RERROR_MASK)
        Log_error("[VUSR]: an ECC error is received from the management interface.\n");
    if (status & CSL_VUSR_STS_LERROR_MASK)
        Log_error("[VUSR]: an inbound packet contains an uncorrectable ECC error.\n");
    if (status & CSL_VUSR_STS_NFEMPTY3_MASK)
        Log_error("[VUSR]: Slave Command FIFO is not empty\n");
    if (status & CSL_VUSR_STS_NFEMPTY2_MASK)
        Log_error("[VUSR]: Slave Data FIFO is not empty.\n");
    if (status & CSL_VUSR_STS_NFEMPTY1_MASK)
        Log_error("[VUSR]: Master Command FIFO is not empty\n");
    if (status & CSL_VUSR_STS_NFEMPTY0_MASK)
        Log_error("[VUSR]: Master Data FIFO is not empty.\n");
    if (status & CSL_VUSR_STS_SPEND_MASK)
        Log_info("[VUSR]: a request has been detected on the Tx VBUSM slave interface.\n");
    if (status & CSL_VUSR_STS_MPEND_MASK)
        Log_error("[VUSR]: a request has been asserted on the Rx VBUSM master interface.\n");
    if (0 == (status & CSL_VUSR_STS_LINK_MASK))
        Log_error("[VUSR]: serial interface initialization sequence has NOT completed successfully.\n");
    if (0 != (status = hHyplnk->ECC_CNTR)) {
        Log_error("[VUSR]: %d single bit error are corrected, %d double bit error are detected\n",
               ((status & CSL_VUSR_ECC_CNTR_SGL_ERR_COR_MASK) >> CSL_VUSR_ECC_CNTR_SGL_ERR_COR_SHIFT),
               ((status & CSL_VUSR_ECC_CNTR_DBL_ERR_DET_MASK) >> CSL_VUSR_ECC_CNTR_DBL_ERR_DET_SHIFT));
        hHyplnk->ECC_CNTR = 0;
    }

    status = hHyplnk->LINK_STS;
    if (status & CSL_VUSR_LINK_STS_TX_RSYNC_MASK)
        Log_error("[VUSR]: the remote device has synced to the transmit training sequence.\n");
    if (status & CSL_VUSR_LINK_STS_TXPLSOK_MASK)
        Log_error("[VUSR]: the Tx PLS layer has linked to the remote device.\n");
    if (status & CSL_VUSR_LINK_STS_RX_LSYNC_MASK)
        Log_error("[VUSR]: the receive has synced to the training sequence.\n");
    if (status & CSL_VUSR_LINK_STS_RX_ONE_ID_MASK)
        Log_error("[VUSR]: lane zero has been identified during training\n");
    #ifdef _DBG
    Uint16 txSerdesSts = status >> CSL_VUSR_LINK_STS_TX_PHY_EN_SHIFT;
    Uint16 rxSerdesSts = status >> CSL_VUSR_LINK_STS_RX_PHY_EN_SHIFT;
    Log_info("[VUSR]: TX Serdes lane enabled mask - %x.\n", txSerdesSts);
    Log_info("[VUSR]: RX Serdes lane enabled mask - %x.\n", rxSerdesSts);
    #endif

    /*disable all portal or remote register operation
     This bit should be set before iloop or reset bits are changed.*/
    hHyplnk->CTL |= CSL_VUSR_CTL_SERIAL_STOP_MASK;
    /*Wait until no Remote Pending Request*/
    while (hHyplnk->STS & CSL_VUSR_STS_RPEND_MASK);
    /*Reset*/
    //hyplnk->CTL |= CSL_VUSR_CTL_RESET_MASK;
    //clear error status
    hHyplnk->STS |= CSL_VUSR_STS_LERROR_MASK | CSL_VUSR_STS_RERROR_MASK;
    /*release from Reset*/
    //hyplnk->CTL &= ~(CSL_VUSR_CTL_RESET_MASK);
    /*enable operation*/
    hHyplnk->CTL &= ~(CSL_VUSR_CTL_SERIAL_STOP_MASK);
    nop(10000000);
}


void Hyplnk_isr(void* arg) {
    CSL_CPINTC_Handle const cic0 = (CSL_CPINTC_Handle)CSL_CP_INTC_0_REGS;
    CSL_CPINTC_disableHostInterrupt(cic0, Hyplnk_HOST_INT);
//    if (!((CSL_CPINTC_RegsOvly)cic0)->ENA_STATUS_REG[CSL_INTC0_VUSR_INT_O / 32]
//                                     & (1 << (CSL_INTC0_VUSR_INT_O & 0x1F)))
//        goto EXIT; constant map, this should not happen
    CSL_CPINTC_clearSysInterrupt(cic0, CSL_INTC0_VUSR_INT_O);

    /*read the HyperLink interrupt vector*/
    Hyplnk_Cmd intVec = (Hyplnk_Cmd) hHyplnk->INT_PRI_VEC;

    while (0 == (intVec & CSL_VUSR_INT_PRI_VEC_NOINTPEND_MASK)) {
        Hyplnk_PeerCmd = intVec;
        if (Hyplnk_ErrorStatusCmd == intVec) //HyperLink error is routed to vector 31.
            Hyplnk_errorRecovery();
        else  if (Hyplnk_IsrHook != NULL)
            Hyplnk_IsrHook(Hyplnk_IsrArg);

        /*write back to clear that interrupt*/
        hHyplnk->INT_PRI_VEC = intVec;
        hHyplnk->INT_CLR = (1 << intVec);

        /* read the HyperLink next interrupt vector */
        intVec = (Hyplnk_Cmd) hHyplnk->INT_PRI_VEC;
    }
    //EXIT:
    CSL_CPINTC_enableHostInterrupt(cic0, Hyplnk_HOST_INT);
}

void
Hyplnk_isrHookAdd(
    void* isre,
    void* arg
){
    Hyplnk_IsrArg = arg;
    Hyplnk_IsrHook = (Isr) isre;
}
